package cn.yunwei.module.config;

import io.minio.*;
import io.minio.credentials.Provider;
import io.minio.credentials.StaticProvider;
import io.minio.errors.MinioException;
import io.minio.messages.Item;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * Minio工具类，用于上传文件到MinIO对象存储。
 *
 * @author wangbinggui
 */
@Component
public class MinioUtil {

    private static final Logger log = LoggerFactory.getLogger(MinioUtil.class);

    // 日志记录器
    private static final Logger logger = LoggerFactory.getLogger(MinioUtil.class);
    // MinIO服务的URL
    @Value("${minio.endpoint}")
    private String endpoint;
    // 访问MinIO服务的accessKey
    @Value("${minio.accessKey}")
    private String accessKey;
    // 访问MinIO服务的secretKey
    @Value("${minio.secretKey}")
    private String secretKey;
    // MinIO服务中的bucket名称
    @Value("${minio.bucketName}")
    private String bucketName;

    // MinIO客户端，用于操作MinIO服务
    private MinioClient minioClient;

    /**
     * 初始化方法，在构造函数执行后调用，用于创建MinioClient实例。
     *
     * @throws Exception 初始化失败时抛出异常。
     */
    @PostConstruct
    public void init() throws Exception {
        // 创建静态提供者，用于提供访问MinIO所需的凭证
        Provider provider = new StaticProvider(accessKey, secretKey, null);
        // 创建MinioClient实例
        minioClient = MinioClient.builder()
                // 设置MinIO服务的URL
                .endpoint(endpoint)
                // 设置访问MinIO服务的凭证
                .credentialsProvider(provider)
                .build();
    }

    /**
     * 上传文件到MinIO对象存储。
     *
     * @param objectName  对象的名称，即上传到MinIO后的文件名。
     * @param stream      要上传的文件的输入流。
     * @param contentType 文件内容的MIME类型。
     */
    public void uploadFile(String objectName, InputStream stream, String contentType) {
        // 使用 try-with-resources 确保流关闭
        try (InputStream inputStream = stream) {
            // 检查存储桶是否存在，不存在则创建
            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!isExist) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            }
            // 获取文件大小
            long size = inputStream.available();
            // 上传文件到MinIO
            minioClient.putObject(PutObjectArgs.builder()
                    // 设置要上传到的存储桶（Bucket）的名称
                    .bucket(bucketName)
                    // 设置上传文件在MinIO服务器上的对象名
                    .object(objectName)
                    // 设置要上传的文件的输入流及其大小
                    .stream(inputStream, size, -1)
                    // 设置文件的MIME类型
                    .contentType(contentType)
                    .build());
        } catch (IOException e) {
            // 处理文件读取错误
            handleException(e);
        } catch (MinioException e) {
            // 处理MinIO操作错误
            handleException(e);
        } catch (Exception e) {
            // 处理其他未知错误
            handleException(e);
        }
    }

    /**
     * 测试MinIO连接。
     */
    public void testMinioConnection() {
        try {
            // 执行一个简单的操作以检查 MinIO 服务器是否可访问
            minioClient.listBuckets();
            logger.info("MinIO 连接测试成功！");
        } catch (MinioException e) {
            // 处理MinIO操作错误
            handleException(e);
        } catch (Exception e) {
            // 处理其他未知错误
            handleException(e);
        }
    }

    // 检查文件夹（前缀）是否存在
    public boolean doesFolderExist(String folderPath) {
        try {
            // MinIO 对象存储中，文件夹是虚拟的概念，只需要检查是否有以该前缀开头的对象
            Iterable<Result<Item>> objects = minioClient.listObjects(
                    ListObjectsArgs.builder().bucket(bucketName).prefix(folderPath).build());
           // 如果有前缀匹配的对象，则目录存在
            return objects.iterator().hasNext();
        } catch (Exception e) {
            log.error("检查文件夹存在时出错: {}", e.getMessage(), e);
            return false;
        }
    }

    // 创建文件夹
    public void createFolder(String folderPath) {
        try {
            // 在 MinIO 中，文件夹是对象存储中的前缀，所以上传一个空文件模拟文件夹
            InputStream emptyContent = new ByteArrayInputStream(new byte[]{});
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            // 模拟文件夹，路径以 "/" 结尾
                            .object(folderPath + "/")
                            .stream(emptyContent, 0, -1)
                            .contentType("application/octet-stream")
                            .build()
            );
            log.info("远程文件夹创建成功: {}", folderPath);
        } catch (Exception e) {
            log.error("创建文件夹时出错: {}", e.getMessage(), e);
        }
    }

    /**
     * 处理异常并记录日志。
     *
     * @param e 异常对象。
     */
    private void handleException(Exception e) {
        if (e instanceof MinioException) {
            logger.error("MinIO 操作错误: {}", ((MinioException) e).toString());
        } else {
            logger.error("发生错误: {}", e.getMessage(), e);
        }
    }


}
